package MawsonClient;

import MawsonClient.GUI.JTextFieldLimit;
import MawsonClient.GUI.MawsonTextField;
import MawsonClient.GUI.RequestedDataElement;
import org.freixas.jcalendar.JCalendarCombo;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.swing.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.awt.*;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Vector;

/**
 * User: Ryan Green
 * Date: Nov 5, 2010
 * Time: 12:51:28 PM
 * Copyright - University of South Australia
 */

//The class to hold all configuration
public class Config {

	public static Color blue = new Color(200,200,255);
	public static Color yellow = new Color(255,255,200);

	public String inputDeliminator;
	public String outputDeliminator;

	public String tempFile;
	public String keyFile;

	public String serverAddress;
	public String username = "";
	public String password = "";
	public String dataSource;
	public String configID;
	
	public String proxyAddress = "";
	public String proxyPort = "";
	public String proxyUsername;
	public String proxyPassword;

	public boolean ignoreFirstLine;

	public int numColumns;
	public int numCompleteColumns;
	public int aggIndex;

	public ArrayList<Field> fields;
	public ArrayList<DerivedField> derivedFields;
	public ArrayList<RequestedDataElement> perFileElements;
	public ArrayList<RequestedDataElement> perLineElements;
	public ArrayList<RequestedDataElement> perAggElements;

	public String watchFolder;
	public String watchFilter;

	public Config(String configFilename, String pathFilename, LogHandler log) {

		fields = new ArrayList<Field>();
		derivedFields = new ArrayList<DerivedField>();
		perFileElements = new ArrayList<RequestedDataElement>();
		perAggElements = new ArrayList<RequestedDataElement>();
		perLineElements = new ArrayList<RequestedDataElement>();

		try {

			//load the following from an xml config file   (pathFilename)
			File fXmlFile = new File(pathFilename);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();

			//get the root element
			Node nud = doc.getDocumentElement();
			serverAddress = getTagValue("serverAddress", (Element) nud);
			keyFile = getTagValue("publickey", (Element) nud);
			inputDeliminator = getTagValue("inputDeliminator", (Element) nud);
			outputDeliminator = getTagValue("outputDeliminator", (Element) nud);
			if (outputDeliminator.equals("tab")) outputDeliminator = "\t";
			watchFolder = getTagValue("watchFolder", (Element) nud);
			if (getTagValue("watchFilter", (Element) nud) == null) {
				watchFilter = "";
			} else {
				watchFilter = getTagValue("watchFilter", (Element) nud);
			}
			username = getTagValue("username", (Element) nud);
			password = getTagValue("password", (Element) nud);
			dataSource = getTagValue("dataSource", (Element) nud);
			configID = getTagValue("configID", (Element) nud);
			tempFile = getTagValue("tempFile", (Element) nud);
			proxyAddress = getTagValue("proxyAddress", (Element) nud);
			proxyPort = getTagValue("proxyPort", (Element) nud);


			//load the file parsing
			fXmlFile = new File(configFilename);
			dbFactory = DocumentBuilderFactory.newInstance();
			dBuilder = dbFactory.newDocumentBuilder();
			doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();

			//get the root element
			nud = doc.getDocumentElement();
			ignoreFirstLine = getTagValue("ignoreFirstLine", (Element) nud).equalsIgnoreCase("true");

			int index = 0;
			//get the derived elements
			NodeList nList = doc.getElementsByTagName("derivedField");
			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {

					String name = "";
					String checkRegex = "";
					ArrayList<String> list = new ArrayList<String>();

					Element eElement = (Element) nNode;

					//index
					index = Integer.parseInt(getTagValue("index", eElement));

					//name
					if (getTagValue("name", eElement) != null) {
						name = getTagValue("name", eElement);
					}

					//baseindex
					int baseIndex = Integer.parseInt(getTagValue("baseIndex", eElement));

					//regex
					NodeList regexList = eElement.getElementsByTagName("regex");
					for (int i = 0; i < regexList.getLength(); i++) {
						list.add(regexList.item(i).getChildNodes().item(0).getNodeValue());
					}

					if (getTagValue("checkRegex", eElement) != null) {
						checkRegex = getTagValue("checkRegex", eElement);
					}

					derivedFields.add(new DerivedField(index, list, baseIndex, name, checkRegex));
				}
			}
			numCompleteColumns = index;

			//do the fields
			nList = doc.getElementsByTagName("field");
			index = 0;

			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					boolean delete = false;
					String name = "";

					Element eElement = (Element) nNode;

					//index
					index = Integer.parseInt(getTagValue("index", eElement));

					//delete
					if (getTagValue("delete", eElement) != null) {
						delete = getTagValue("delete", eElement).equals("True") || getTagValue("delete", eElement).equals("true") || getTagValue("delete", eElement).equals("TRUE");
					}

					//name
					if (getTagValue("name", eElement) != null) {
						name = getTagValue("name", eElement);
					}

					fields.add(new Field(index, delete, name));
				}
			}
			numColumns = index + 1;

			//do the per file swing elements
			nList = doc.getElementsByTagName("requestedMetadata");
			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					String label, comp, per;
					Component compon;
					Vector<String> comboOptions = null;
					String defaultOption = "";
					String required = "false";
					int textFieldLength = 100;

					Element eElement = (Element) nNode;

					label = getTagValue("label", eElement);

					comp = getTagValue("component", eElement);

					per = getTagValue("scope", eElement);

					if (getTagValue("default", eElement) != null) {
						defaultOption = getTagValue("default", eElement);
					}

					if (getTagValue("required", eElement) != null) {
						required = getTagValue("required", eElement);
					}

					if (getTagValue("textFieldLength", eElement) != null) {
						textFieldLength = Integer.parseInt(getTagValue("textFieldLength", eElement));
					}

					if (comp.equals("comboBox")) {
						comboOptions = new Vector<String>();
						NodeList nudList = eElement.getElementsByTagName("comboBoxOption");
						for (int i = 0; i < nudList.getLength(); i++) {
							comboOptions.add(nudList.item(i).getChildNodes().item(0).getNodeValue());
						}
						compon = new JComboBox(comboOptions);
					} else if (comp.equals("textField")) {
						compon = new MawsonTextField(defaultOption, required.equals("true"), new JTextFieldLimit(textFieldLength));
					} else if (comp.equals("dateField")) {
						JCalendarCombo jcal = new JCalendarCombo();
						jcal.setDateFormat(new SimpleDateFormat("dd MMM yyyy"));
						compon = jcal;
					} else {
						throw new Exception("unknown component type in xml config file: " + comp);
					}

					if (per.equals("PerLine")) {
						perLineElements.add(new RequestedDataElement(label, compon, -1, textFieldLength, comboOptions, defaultOption, required.equals("true"), log));
					} else if (per.equals("PerAggregate")) {
						int aggIndex = Integer.parseInt(getTagValue("aggregateIndex", eElement));
						perAggElements.add(new RequestedDataElement(label, compon, aggIndex, textFieldLength, comboOptions, defaultOption, required.equals("true"), log));
					} else if (per.equals("PerFile")) {
						perFileElements.add(new RequestedDataElement(label, compon, -1, textFieldLength, comboOptions, defaultOption, required.equals("true"), log));
					} else {
						throw new Exception("Unknown scope value in xml config file: " + per);
					}
				}
			}

			//ensure all aggregates are aggregated on the same index
			if (perAggElements.size() > 0){
				aggIndex = perAggElements.get(0).aggIndex;
				for (RequestedDataElement data : perAggElements){
					if (data.aggIndex != aggIndex) throw new Exception("Not all aggregates are aggregated on the same index, multi-level aggregation is not supported.");
				}
			}

		} catch (Exception e) {
			log.Log("An error occurred when parsing the configuration file.", true);
			log.LogAndExit(e.getStackTrace());
		}
	}

	private static String getTagValue(String sTag, Element eElement) {
		Node nud = eElement.getElementsByTagName(sTag).item(0);

		if (nud == null) return null;

		return nud.getChildNodes().item(0).getNodeValue();
	}

}
